package RabbitMQ

import (
	"fmt"
	"github.com/streadway/amqp"
	"log"
)

// MQURL 1 定义连接 格式: amqp://账号:密码@IP:端口/虚拟主机
const MQURL = "amqp://develop:develop@192.168.56.103:5673/develop"

// RabbitMQ 结构体成员
type RabbitMQ struct {
	conn    *amqp.Connection
	Channel *amqp.Channel
	// 队列名称
	QueueName string
	// 交换器
	Exchange string
	// key
	Key string
	// 连接信息
	MqUrl string
}

// NewRabbitMQ 创建rabbitMQ结构体实例,基础实例
func NewRabbitMQ(queueName, exChange, key string) *RabbitMQ {
	rabbitMq := &RabbitMQ{QueueName: queueName, Exchange: exChange, Key: key, MqUrl: MQURL}
	var err error
	rabbitMq.conn, err = amqp.Dial(rabbitMq.MqUrl)
	rabbitMq.failOnErr(err, "创建连接错误")
	rabbitMq.Channel, err = rabbitMq.conn.Channel()
	rabbitMq.failOnErr(err, "获取channel失败")
	return rabbitMq
}

// Destroy 中断连接
func (r *RabbitMQ) Destroy() {
	err := r.Channel.Close()
	if err != nil {
		return
	}
	err = r.conn.Close()
	if err != nil {
		return
	}
}

// ReConnect 重连
func (r *RabbitMQ) ReConnect() {
	// 创建rabbitMQ实例
	conn, err := amqp.Dial(r.MqUrl)
	if err != nil {
		panic(err)
	}
	// 创建rabbitMQ通道
	channel, err := conn.Channel()
	if err != nil {
		panic(err)
	}
	r.conn = conn
	r.Channel = channel
}

// 错误处理 打印日志,或写入db,甚至是发送邮件或者短信
func (r *RabbitMQ) failOnErr(err error, message string) {
	if err != nil {
		log.Fatalf("%s:%s", message, err)
		fmt.Println(err)
		panic(fmt.Sprintf("%s:%s", message, err))
	}
}

// NewRabbitMQSimple 简单模式Step:1.创建简单模式下的rabbitMQ实例
func NewRabbitMQSimple(queueName string) *RabbitMQ {
	return NewRabbitMQ(queueName, "", "")
}

// PublishSimple 简单模式Step:2.简单模式下生产代码
func (r *RabbitMQ) PublishSimple(message string) {
	//1.申请队列，如果队列不存在会自动创建，如果存在则跳过创建直接使用
	//保证队列存在，消息能发送到队列中
	_, err := r.Channel.QueueDeclare(
		r.QueueName,
		false, // 是否持久化
		false, // 是否自动删除
		false, // 是否具有排他性
		false, // 是否阻塞处理
		nil,   // 额外属性
	)
	if err != nil {
		fmt.Println("PublishSimple错误", err)
		//r.failOnErr(err, "Failed to declare a queue")
	}

	//2.发送消息到队列中
	err = r.Channel.Publish(r.Exchange, r.QueueName,
		false, // 如果为true，根据exchange类型和routkey规则，如果无法找到符合条件的队列，那么会把发送的消息返回给发送者
		false, // 如果为true，当exchange发送消息到队列后发现队列上没有绑定消费者，则会把消息返回给发送者
		amqp.Publishing{
			//消息内容
			Body: []byte(message),
		})
	if err != nil {
		return
	}
}

// ConsumeSimple 简单模式Step:3.消费者代码
func (r *RabbitMQ) ConsumeSimple() {
	//1.申请队列，如果队列不存在会自动创建，如果存在则跳过创建直接使用
	//保证队列存在，消息能发送到队列中
	_, err := r.Channel.QueueDeclare(r.QueueName, false, false, false, false, nil)
	if err != nil {
		r.failOnErr(err, "Failed to declare a queue")
	}

	//接收消息
	msg, err := r.Channel.Consume(r.QueueName, "", true, false, false, false, nil)
	if err != nil {
		r.failOnErr(err, "Failed to register a consumer")
	}

	//遍历消息,启用协程处理消息
	go func() {
		for d := range msg {
			// 实现我们的业务逻辑
			log.Printf("Received a message: %s", d.Body)
		}
	}()
}
